package com.googlecode.wmlynar.j2meunitgenerator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

/**
 * Collects list of test cases given the directory and returns an array of TestClass objects.
 *
 * @author Wojciech Mlynarczyk (woj)
 *
 */
public class TestClassCollector {

	public ArrayList<TestClass> collect(JavaDocBuilder builder) {
		ArrayList<TestClass> result= new ArrayList<TestClass>();

		JavaClass[] classes = builder.getClasses();

		// sort classes by name for improved reporting
		Arrays.sort(classes, new Comparator() {

            public int compare(Object o1, Object o2) {
                if (!(o1 instanceof JavaClass) || !(o2 instanceof JavaClass)) {
                    throw new ClassCastException();
                }

                JavaClass jc1 = (JavaClass) o1;
                JavaClass jc2 = (JavaClass) o2;
                return jc1.getName().compareToIgnoreCase(jc2.getName());
            }});


		for(JavaClass clas : classes) {
			if(!(clas.getName().startsWith("Test") || clas.getName().endsWith("Test"))) {
				System.out.println("ignoring " + clas.getFullyQualifiedName()
                        + " (doesn't start or end with Test)");
				continue;
			}
            if (clas.isAbstract()) {
                System.out.println("ignoring " + clas.getFullyQualifiedName() + " (is abstract)");
                continue;
            }
			System.out.println("processing " + clas.getFullyQualifiedName());
			JavaMethod[] methods = clas.getMethods();
			ArrayList<JavaMethod> testMethods = new ArrayList<JavaMethod>();
			boolean hasConstructor = false;
			boolean hasNoArgumentConstructor = false;
			for(JavaMethod method : methods) {
				// check that method has no argument constructor
				if(method.isConstructor()) {
					hasConstructor = true;
					if(method.isPublic() && method.getParameters().length==0) {
						hasNoArgumentConstructor = true;
					}
				}
				if(!method.getName().startsWith("test")) {
					continue;
				}
				String fullName = clas.getFullyQualifiedName() + "." + method.getName();
				if(!method.isPublic()) {
					throw new RuntimeException("Test method " + fullName + " is not public");
				}
				if(method.getParameters().length!=0) {
					throw new RuntimeException("Test method " + fullName
                            + " should have no parameters");
				}
				if(!method.getReturns().getGenericValue().equals("void")) {
					throw new RuntimeException("Test method " + fullName
                            + " should have void return type");
				}
				testMethods.add(method);
			}
			if(testMethods.size()==0) {
				throw new RuntimeException("No tests found in class "
                        + clas.getFullyQualifiedName() + " (consider making the class abstract)");
			}
			if(hasConstructor && !hasNoArgumentConstructor) {
				throw new RuntimeException("A public no argument constructor is required in class "
                        + clas.getFullyQualifiedName());
			}
			TestClass c = new TestClass();
			c.setName(clas.getFullyQualifiedName());
			for(JavaMethod method : testMethods) {
				//System.out.println(method.getName());
				c.getTestMethods().add(method.getName());
			}
			result.add(c);
		}
		return result;
	}

}
